﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Collections.Specialized;

namespace TrainGame.Common
{
    /// <summary>
    /// Encapsulates all game save logic.
    /// </summary>
    public class FileUtils
    {

        /// <summary>
        /// Indicates if an error occurred during the last operation.
        /// This is a read-only field.
        /// </summary>
        public static bool ErrorsOccurred
        {
            get { return (FileUtils.Errors.Count > 0); }
        }

        private static List<Exception> _errors = new List<Exception>();

        /// <summary>
        /// The list of errors that occurred during the last operation.
        /// The list may  be cleared to erase all errors.
        /// </summary>
        public static List<Exception> Errors
        {
            get { return FileUtils._errors; }
        }


        /// <summary>
        /// maximum number of files to use in the queue.
        /// </summary>
        static int _maxFiles = 10;

        /// <summary>
        /// Maximum number of files to save. This can be changed dynamically.
        /// </summary>
        public static int MaxFiles
        {
            get { return FileUtils._maxFiles; }
            set { FileUtils._maxFiles = value; }
        }

        /// <summary>
        /// Calculates the next file to use. Deletes older files that exceed the queue limit.
        /// </summary>
        /// <param name="directory">The base path to where files can begin to be saved. This must
        /// be a valid directory or an exception will be thrown.</param>
        /// <param name="gameName">The name of this game series</param>
        /// <param name="fileExtension"> The file extension to associate with the file</param>
        /// <returns>the filename to use</returns>
        static public string GetNextFileName( string directory, string gameName,string fileExtension )
        {
            // validate arguments
            if ( string.IsNullOrEmpty( directory ) )
                throw new ArgumentNullException( "directory cannot be empty or null." );
            if ( string.IsNullOrEmpty( gameName ) )
                throw new ArgumentNullException( "gameName cannot be empty or null." );
            if ( string.IsNullOrEmpty( fileExtension ) )
                throw new ArgumentNullException( "fileExtension cannot be empty or null." );

            Errors.Clear();

            // get current file number from last saved file in directory
            int num = GetLastFileNumber( directory, gameName );
            num++;  // next file in sequence
            string fileName = BuildFileName( gameName, num, fileExtension );
            string filePath = Path.Combine( directory, fileName );

            // cleanup files that exceed the limit
            string[] files = Directory.GetFiles( directory );
            int deleteNum = num - MaxFiles;
            foreach ( string file in files )
            {
                // ensure this is one of the game files
                if ( IsFileInGameSet( file, gameName ) ) // case-insensitive compare
                {
                    int fileNum = GetFileNumber( file );
                    // This is a very simple implementation. It does not actually count the number
                    // of files, it goes strictly off the file name.
                    if ( fileNum < deleteNum || fileNum > num )
                    {
                        try
                        {
                            File.Delete( file );
                            XTrace.Trace("Deleted file {0}.", file );
                        }
                        catch ( Exception ex )
                        {
                            XTrace.Trace( "Unable to delete file {0}. Reason={1}.", file, ex.Message );
                            Exception e = new Exception( "Unable to delete old file.", ex );    // wrap the exception to add context
                            Errors.Add( e );
                            // keep trying other files
                        }
                    }
                }
            }

            return filePath;

        }

        /// <summary>
        /// Indicates if the filename should belong to the gameset.
        /// Compare is case-insensitive.
        /// </summary>
        /// <param name="fileName">The name of the file</param>
        /// <param name="gameName">The name of the game set.</param>
        /// <returns>true if it is part of the game set, otherwise false</returns>
        static private bool IsFileInGameSet( string fileName, string gameName )
        {
            fileName = Path.GetFileNameWithoutExtension( fileName ); // get rid of path portion
            int ndx = fileName.IndexOf( '_' );  // only examine the name up to the underscore
            if ( ndx >= 0 ) // underscore???
            {
                string gamePortion = fileName.Substring( 0, ndx );
                // do case-insensitive compare
                return ( string.Compare( gameName, gamePortion, true ) == 0 );
            }
            return false;
        }


        /// <summary>
        /// Each filename must adhere  to the pattern of naming files. This is
        /// file={0}_{1}{2}, where
        /// {0} = base file name
        /// {1} = current file number
        /// {2} = file extension.
        /// </summary>
        /// <param name="gameName"></param>
        /// <param name="num"></param>
        /// <param name="fileExtension"></param>
        /// <returns></returns>
        private static string BuildFileName( string gameName, int num, string fileExtension )
        {
            return string.Format( "{0}_{1}.{2}", gameName, num, fileExtension );
        }

        /// <summary>
        /// Gets the last filename in the series of existing file names.
        /// This routine ignores the extension and just extracts the file number
        /// </summary>
        /// <param name="directory">A path to a directory.</param>
        /// <param name="gameName">The name used as the base file name</param>
        /// <returns>A filename, or null if none exists</returns>
        private static int GetLastFileNumber( string directory, string gameName )
        {
            string[] files = Directory.GetFiles( directory );
            int lastnum = -1;

            try
            {
                foreach ( string f in files )
                {
                    string fileName = Path.GetFileNameWithoutExtension( f ); // get rid of path portion
                    // ensure that this file is related to the current game
                    if ( IsFileInGameSet(fileName,gameName ) )
                    {
                        int num = GetFileNumber( fileName );
                        if ( num > lastnum )
                            lastnum = num;
                    }
                }

            }
            catch ( Exception ex )
            {
                XTrace.Trace( ex.ToString() );
            }
            return lastnum;
        }




        /// <summary>
        /// Extracts the file number portion of the file name.
        /// If anything fails then a 0 is returned.
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private static int GetFileNumber( string fileName )
        {
            int filenum = 0;
            string fileNameNoExt = Path.GetFileNameWithoutExtension( fileName ); // get rid of path portion
            if ( !string.IsNullOrEmpty( fileNameNoExt ) )
            {
                int ndx = fileNameNoExt.IndexOf( '_' );
                if ( ndx >= 0 )
                {
                    string number = fileNameNoExt.Substring( ndx+1 );
                    if ( number.Length > 0 )
                    {   // what remains should be numeric 
                        int num;
                        if ( int.TryParse( number, out num ) )
                        {
                            filenum = num;
                        }
                    }
                }
            }
            return filenum;
        }   //  GetFileNumber




        public static bool GetSavedGames(string sName, string sBasePath, NameValueCollection savedGames)
        {
            bool    bReturn = false;
            string  sGameName = sName + "'s game";
            string  directory = Path.Combine(Path.Combine(sBasePath, "SavedGames"), sGameName);

            if (Directory.Exists(directory))
            {
                string[] files = Directory.GetFiles(directory);
                
                if (files.Length > 0)
                {
                    bReturn = true;
                }

                foreach (string sFile in files)
                {
                    DateTime    modifiedTime = Directory.GetCreationTime (sFile);
                    string  sInfo = Path.GetFileNameWithoutExtension (sFile);
                    sInfo = sInfo + ": " + modifiedTime.ToShortDateString () + ": " + modifiedTime.ToLongTimeString ();
                    savedGames.Add(sInfo, sFile);
                }
            }
            
            return bReturn;
        }
    }   //  class FileUtils
}
